package io.github.loserYa.log;

import com.alibaba.fastjson2.JSONObject;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import io.github.loserYa.log.handler.ShardingHandler;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 实体、sql转换类
 *
 * @author loser
 * @date 2024/4/25
 */
public class EntitySqlParser {

    private static final Cache<String, String> localCache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterAccess(10, TimeUnit.MINUTES).build();

    static {
        localCache.put("id", "id");
        localCache.put("num", "num");
        localCache.put("count", "count");
        localCache.put("userId", "user_id");
        localCache.put("roomId", "room_id");
        localCache.put("giftId", "gift_id");
        localCache.put("logTime", "log_time");
        localCache.put("logDate", "log_date");
        localCache.put("anchorId", "anchor_id");
        localCache.put("createTime", "create_time");
        localCache.put("createDate", "create_date");
        localCache.put("updateTime", "update_time");
        localCache.put("updateDate", "update_date");
    }

    public static SqlBuild generateInsertSQL(JSONObject entity, String table, ShardingHandler shardingHandler) {

        StringBuilder columns = new StringBuilder();
        StringBuilder values = new StringBuilder("(");

        Object shardingValue = null;
        for (Map.Entry<String, Object> field : entity.entrySet()) {
            String fieldName = field.getKey();
            Object value = field.getValue();
            if (value != null) {
                String column = convertToUnderscore(fieldName);
                if (Objects.nonNull(shardingHandler) && shardingHandler.column().equals(column)) {
                    shardingValue = value;
                }
                if (columns.length() > 0) {
                    columns.append(",");
                    values.append(",");
                }
                columns.append(format(column));
                values.append(formatValue(value));
            }
        }
        values.append(")");

        String tableName = Objects.nonNull(shardingValue) ? shardingHandler.getPhysicalTable(table, shardingValue) : table;
        return SqlBuild.buildInsert(format(tableName), columns.toString(), values.toString());

    }

    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static String formatValue(Object value) {
        if (value instanceof String) {
            return "'" + value + "'";
        } else if (value instanceof Date) {
            return "'" + sdf.format((Date) value) + "'";
        } else {
            return value.toString();
        }
    }

    public static String convertToUnderscore(String camelCase) {

        String present = localCache.getIfPresent(camelCase);
        if (!StringUtils.isEmpty(present)) {
            return present;
        }
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < camelCase.length(); i++) {
            char currentChar = camelCase.charAt(i);
            if (Character.isUpperCase(currentChar)) {
                if (i > 0) {
                    result.append("_");
                }
                result.append(Character.toLowerCase(currentChar));
            } else {
                result.append(currentChar);
            }
        }
        localCache.put(camelCase, result.toString());
        return result.toString();

    }

    public static SqlBuild generateUpdateSQL(JSONObject entity, JSONObject update, String table, ShardingHandler shardingHandler) {

        StringBuilder updates = new StringBuilder();
        StringBuilder condition = new StringBuilder();

        Object shardingValue = null;
        for (Map.Entry<String, Object> field : entity.entrySet()) {
            String fieldName = field.getKey();
            Object value = field.getValue();
            if (value != null) {
                if (updates.length() == 0) {
                    updates.append(format(convertToUnderscore(fieldName))).append("=").append(formatValue(value));
                } else {
                    updates.append(" , ").append(format(convertToUnderscore(fieldName))).append("=").append(formatValue(value));
                }
            }
        }
        for (Map.Entry<String, Object> field : update.entrySet()) {
            String fieldName = field.getKey();
            Object value = field.getValue();
            if (value != null) {
                String column = convertToUnderscore(fieldName);
                if (Objects.nonNull(shardingHandler) && shardingHandler.column().equals(column)) {
                    shardingValue = value;
                }
                if (condition.length() == 0) {
                    condition.append(format(column)).append("=").append(formatValue(value));
                } else {
                    condition.append(" AND ").append(format(column)).append("=").append(formatValue(value));
                }
            }
        }

        String tableName = Objects.nonNull(shardingValue) ? shardingHandler.getPhysicalTable(table, shardingValue) : table;
        return SqlBuild.buildUpdate(format(tableName), updates.toString(), condition.toString());

    }

    private static String format(String column) {
        return String.format("`%s`", column);
    }

}
